import { NextFunction, Request, Response } from 'express';
import { User } from './objects/user';
import { Article } from './objects/article';
import { MongoDb } from './utils/mongo';
import { AuthenticatedRequest, FailedResponse, RegisterSession, Session, SuccessfulResponse } from './types';
import { ParamsDictionary } from 'express-serve-static-core';
import { getOrFail } from './utils/nullable';
import { Config } from './config';

const express = require('express');
const routers = express.Router();
const parser = require('body-parser');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const captcha = require('svg-captcha');
const multer = require('multer');
const upload = multer();

export async function configRouter(config: Config) {
	await MongoDb.init(config).then(() => {
		console.log('Configuring routers');

		/// session
		routers.use(session({
			secret: 'kbAAOg',
			store: new MongoStore({client: MongoDb.client}),
			saveUninitialized: false,
			resave: false,
			rolling: true,
			cookie: {
				maxAge: 900_000, // 15 minuets
			},
		}));

		routers.use('/assets', express.static(__dirname + '/../assets'));

		routers.get('/favicon.ico', (_: Request, response: Response) => {
			response.status(404);
			response.send();
		});

		/// post
		routers.use(parser.urlencoded({extended: true}));

		/// logging
		routers.use((request: Request, _: Response, next: NextFunction) => {
			console.log(`Request(${request.method}:${request.url})@${request.ip}:${request.session ? request.session.name : '(null)'}`);
			if (request.session) {
				let session = request.session as Session;
				session.lastHeartbeatTime = Math.floor(Date.now() / 1000);
				if (session.name) {
					User.getUserFromName(session.name).then(user => {
						if (user) {
							(request as AuthenticatedRequest).user = user;
							next();
						} else {
							next({
								what: 'Forged user name in session!',
							});
						}
					});
				} else {
					next();
				}
			} else {
				next();
			}
		});

		/// redirecting
		routers.use((request: Request, response: Response, next: NextFunction) => {
			if (request.session) {
				let session = request.session as Session;
				if (session.redirection && session.redirection.length > 0) {
					if (request.method.toUpperCase() === 'GET') {
						let location = session.redirection;
						session.redirection = undefined;
						response.redirect(location);
					} else {
						next();
					}
				} else {
					next();
				}
			} else {
				next();
			}
		});

		/// authenticator
		routers.use((request: Request, response: Response, next: NextFunction) => {
			if (request.method.toUpperCase() === 'GET') {
				next();
			} else {
				let req = request as AuthenticatedRequest;
				if (req.user) {
					next();
				} else {
					if (!(request.url.endsWith('login') || request.url.endsWith('register'))) {
						console.log(`${request.url} redirected to /login`);
						response.redirect('/login');
					} else {
						console.log(`${request.url} passed`);
						next();
					}
				}
			}
		});

		/// index, page
		routers.get('/', (request: Request, response: Response) => {
			response.render('index', {
				title: '主页',
				username: request.session ? (request.session as Session).name : undefined,
			});
		});

		/// get article, page
		routers.get('/kb/:id', (request: AuthenticatedRequest, response: Response) => {
			let params = request.params as ParamsDictionary;
			let id = params.id;
			if (id === 'all') {
				Article.getAllArticles().then(articles => {
					response.render('allArticles', {
						title: '所有文档',
						username: request.session ? (request.session as Session).name : undefined,
						articles: articles,
					});
				});
			} else if (id === 'random') {
				// TODO
			} else {
				let numbering;
				try {
					numbering = Number(id);
					Article.getArticleById(numbering).then(article => {
						if (article) {
							response.render('article', {
								title: article.getTitle(),
								articleContent: article.renderHtml(),
								username: request.session ? (request.session as Session).name : undefined,
							});
						} else {
							response.render('error404');
						}
					});
				} catch (e) {
					response.status(400);
					return;
				}
			}
		});

		/// get user info, json
		routers.get('/u/:name', (request: AuthenticatedRequest, response: Response) => {
			let params = request.params as ParamsDictionary;
			let name = params.name;
			User.getUserFromName(name).then(user => {
				if (user) {
					response.send(user);
				} else {
					response.send('404 NOT FOUND');
				}
			});
		});

		/// get register captcha, svg image
		routers.get('/challenge', (request: Request, response: Response) => {
			if (request.session) {
				let session = request.session as RegisterSession;
				let challenge = captcha.create({
					noise: 3,
					ignoreChars: '0oO1ilIL',
				});
				session.challengeAnswer = challenge.text;
				response.type('svg');
				response.send(challenge.data);
			} else {
				response.send('?Session');
			}
		});

		/// get register page, page
		routers.get('/register', (request: Request, response: Response) => {
			if (!request.session) {
				// debug
				console.log('?Session');
			}
			response.render('register', {
				title: '注册',
			});
		});

		/// get user config page, page
		routers.get('/me', (request: AuthenticatedRequest, response: Response) => {
			if (request.user) {
				response.render('me', {
					title: '个人主页',
					user: request.user,
					username: request.user.getName(),
				});
			} else {
				if (request.session) {
					let session = request.session as Session;
					session.redirection = '/me';
				}
				response.render('login', {
					title: '登录',
				});
			}
		});

		/// get login page, page
		routers.get('/login', (request: Request, response: Response) => {
			response.render('login', {
				title: '登录',
			});
		});

		/// user verify, page
		routers.get('/verify/:uid', (request: Request, response: Response) => {
			let params = request.params as ParamsDictionary;
			let uid = params['uid'];
			if (uid) {
				User.getUserFromUid(uid).then(user => {
					user.verifyEmail();
					response.render('verify', {
						title: '邮箱验证',
						username: user.getName(),
					});
				});
			} else {
				response.send('?UID');
			}
		});

		/// login handler, json in standard response
		routers.post('/login', upload.none(), (request: Request, response: Response) => {
			if (request.session) {
				let session = request.session as Session;
				let params = request.body as ParamsDictionary;
				Promise.all([
					getOrFail(params, 'username'),
					getOrFail(params, 'password'),
				]).then(function ([username, password]) {
					User.isUserPresent(username).then(present => {
						if (present) {
							User.getUserFromName(username).then(user => {
								if (user.deleted()) {
									response.send(new FailedResponse('用户已停用'));
								} else {
									if (user.verify(password)) {
										session.name = user.getName();
										response.send(new SuccessfulResponse(user));
									} else {
										session.name = undefined;
										response.send(new FailedResponse('密码错误'));
									}
								}
							});
						} else {
							session.name = undefined;
							response.send(new FailedResponse('用户不存在'));
						}
					});
				});
			} else {
				response.send(new FailedResponse('Session', -500));
			}
		});

		/// register handler, json in standard response
		routers.post('/register', upload.none(), (request: Request, response: Response) => {
			if (request.session) {
				let session = request.session as RegisterSession;
				if (session.challengeAnswer) {
					let params = request.body as ParamsDictionary;
					Promise.all([
						getOrFail(params, 'username'),
						getOrFail(params, 'password'),
						getOrFail(params, 'email'),
						getOrFail(params, 'challenge'),
					]).then(function ([username, password, email, challenge]) {
						if (challenge.toLowerCase() === session.challengeAnswer.toLowerCase()) {
							User.isUserPresent(username).then(presence => {
								if (!presence) {
									let user = User.createUser(username, password, email);
									user.commit();
									response.send(new SuccessfulResponse(user));
								} else {
									response.send(new FailedResponse('用户名已被注册', -100));
								}
							});
						} else {
							response.send(new FailedResponse('验证码错误', -101));
						}
					}).catch(() => {
						response.send(new FailedResponse('表单不完整', -102));
					});
				} else {
					response.send(new FailedResponse('未填写验证码', -500));
				}
			} else {
				response.send(new FailedResponse('服务器错误', -500));
			}
		});

		/// find user, json
		routers.post('/find/user', upload.none(), (request: Request, response: Response) => {
			// TODO
		});

		/// write artile, json
		routers.post('/kb/:id', upload.none(), (request: Request, response: Response) => {
			// TODO
		});

		/// find article, json
		routers.post('/find/article', upload.none(), (request: Request, response: Response) => {
			// TODO, and it might be really hard
		});

		/// list all articles
		routers.get('/articles', (request: AuthenticatedRequest, response: Response) => {
			MongoDb.connect().then(db => {
				response.render('articles', {
					articles: db.collection('articles').find({}),
				});
			});
		});

		/// debug use, json
		routers.get('/status', (request: AuthenticatedRequest, response: Response) => {
			MongoDb.connect().then(db => {
				db.collection('articles').countDocuments().then(articleCount => {
					db.collection('users').countDocuments().then(userCount => {
						response.send({
							clientIp: request.ip,
							uptime: process.uptime(),
							time: Math.floor((new Date()).getTime() / 1000),
							articleCount: articleCount,
							userCount: userCount,
							session: request.session,
							who: request.user ? request.user.getName() : '(anonymous)',
						});
					});
				});
			});
		});

		/// logout, page
		routers.get('/logout', (request: Request, response: Response) => {
			let session = request.session as Session;
			if (session) {
				session.name = undefined;
				session.destroy(() => {
				});
			}
			response.render('logout', {
				title: '登出',
				username: '',
			});
		});

		/// 404 Error handler, place at the bottom!
		routers.get('*', (request: Request, response: Response) => {
			response.status(404);
			if (request.accepts('html')) {
				response.render('error404');
			} else if (request.accepts('json')) {
				response.send({status: 404});
			} else {
				response.type('txt').send('Not found');
			}
		});

		/// should not reach here
		routers.all('*', (request: Request, response: Response) => {
			response.send({status: 500, msg: 'Running in the wild', url: request.url, method: request.method});
		});

		console.log('Router configured');
	});
	return routers;
}
